
/* *
    canvas 弹幕原理, 首先先清空整个画布 => 然后将每一条弹幕画在画布上 (初始化每一条弹幕,即给弹幕添加一些属性,
        例如:弹幕在画布上面的长宽,弹幕里面文字的颜色,字体大小,弹幕在画布上的位置,弹幕在画布上的出现时间,弹幕
        内容 等等.. 若初始化过则不用初始化,直接渲染) => 然后递归执行上面两步操作( 利用requestAnimationFrame
        来替代定时器,优化渲染效果),将弹幕一条一条的渲染到画布上,每移动一次,对应弹幕的x值减去1,当弹幕移出了画布,
        则停止渲染该条弹幕 => 当视频未开始或者暂停了也暂停弹幕
    发送弹幕: 
        当发送弹幕时候,首先用websocket发送消息给服务器,服务器存好后,再将弹幕渲染到屏幕上
        监听拖动进度条事件,当进度条被拖动时候,根据当前视频时间和每条弹幕的事件进行对比,是否要初始化该条弹幕或者不用管,
        但是首先应该清除画布上的所有弹幕.
        视频一打开,服务器会将所有的弹幕推送给我们,我们要拿到这些弹幕进行初始化渲染

    1. 创建 Canvas 类,用于渲染(递归渲染整个画布上面的每一条弹幕)

*/
// text: 弹幕内容, time: 何时开始发送(单位 s )  speed: 速度 

// 这是假数据,模拟服务器端推送的消息
let data = [
    { text: 'hello word', time: 0, color: 'red', fontSize: 20, speed: 2 },
    { text: '弹幕', time: 12 }
]
let socket = new WebSocket('ws://locolhost:3000')
socket.onopen = function () {
    
}
socket.onmessage=function(data) {
    data = JSON.parse(data).map(i=>JSON.parse(i))
}

// 自己封装 获取dom元素
let $ = document.querySelector.bind(document)

let canvas = $('#canvas');
let video = $('#video');

// 设置每个弹幕 的轨迹,属性,并初始化每个弹幕,渲染每个弹幕到画布上
class Barrage {
    constructor(barrage, ctx) {
        this.barrage = barrage
        this.ctx = ctx

        this.text = barrage.text
        this.time = barrage.time
    }

    // 初始化当前这个弹幕,
    init() {
        // 为这条弹幕添加一些属性
        this.color = this.barrage.color || this.ctx.color
        this.fontSize = this.barrage.fontSize || this.ctx.fontSize
        this.speed = this.barrage.speed || this.ctx.speed

        // 求出 当前这条弹幕的长宽
        let span = document.createElement('span');
        span.innerText = this.text
        span.style.font = this.fontSize + 'px Microsoft YaHei'
        span.style.position = 'absolute'
        document.body.appendChild(span)
        this.width = span.clientWidth // 该条弹幕宽度
        this.height = span.clientHeight// 该条弹幕高度
        document.body.removeChild(span)

        // 弹幕出现的位置
        this.x = this.ctx.canvas.width
        // 注意,canvas里面文字的y是文字的基线,而不是左上角顶点
        this.y = this.ctx.canvas.height * Math.random()

        // 弹幕是都继续在画布上渲染
        if (this.y <= this.height) { // 不让文字超出canvas上边框
            this.y = this.height
        }
        if (this.y > this.ctx.canvas.height) { // 不让文字超出canvas下边框
            this.y = this.ctx.canvas.height
        }
    }

    // 将当前这个弹幕渲染到屏幕上
    render() {
        this.ctx.contxt.font = `${this.fontSize}px Microsoft YaHei`;
        this.ctx.contxt.fillStyle = this.color
        this.ctx.contxt.fillText(this.text, this.x, this.y)
    }

}

// 用于控制整个canvas上的弹幕
class Canvas {
    constructor(canvas, video, options) {
        if (!canvas || !video) { // 若没有canvas和视频播放器,则不进行以后的所有操作
            return
        }
        this.canvas = canvas
        this.video = video
        this.options = options
        this.canvas.width = video.clientWidth
        this.canvas.height = video.clientHeight
        this.contxt = canvas.getContext('2d') // canvas上下文

        // 是否暂停, 默认是暂停
        this.isSuspend = true

        // 设置弹幕默认属性,但是弹幕内容和弹幕出现的时间是必须给的,其余属性有就用,没有就取默认值
        let defaultOpt = {
            color: 'gold', // 默认颜色
            speed: 2, // 移动速度
            fontSize: 20, // ,欧仁字号
            data: []
        }
        // 合并对象 并挂在在实例上面
        Object.assign(this, defaultOpt, options)
        // 用于存放所有弹幕
        this.allBarrage = this.options.data.map(barrage => new Barrage(barrage, this))
        // 渲染弹幕
        this.render() 
    }

    // 渲染弹幕
    render() {
        // 先清空画布
        this.contxt.clearRect(0, 0, this.canvas.width, this.canvas.height)
        //  渲染弹幕
        this.renderBarrage()
        // 如果当前是播放状态则开始递归渲染
        if (this.isSuspend === false) {
            requestAnimationFrame(this.render.bind(this))
        }

    }

    // 真正的渲染方法
    renderBarrage() {
        // 若当前视频的时间大于该条弹幕该出现的时间,则渲染该条弹幕
        // 1. 拿到当前视频播放的时间
        let currentTime = this.video.currentTime
        this.allBarrage.forEach(barrabeItem => {
            /* barrabeItem是每个弹幕的实例 */
            if (!barrabeItem.flag && currentTime >= barrabeItem.time) {
                // 渲染之前,若没有初始化该条弹幕则先初始化该条弹幕
                if (!barrabeItem.isInit) {
                    barrabeItem.init()
                    barrabeItem.isInit = true
                }
                // 更新位置
                barrabeItem.x -= barrabeItem.speed
                barrabeItem.render() // 渲染自己到画布上
                // 当弹幕移动出了canvas,则停止渲染
                if (barrabeItem.x <= -barrabeItem.width) {
                    barrabeItem.flag = true
                }
            }
        })
    }

    // 添加弹幕(用于输入框添加弹幕,并将弹幕画在画布上)
    addBarrage(barrage) {
        this.allBarrage.push(new Barrage(barrage, this))
    }

    // 重置弹幕(用于进度条拖动时候控制弹幕是否该出现)
    reset() {
        this.contxt.clearRect(0, 0, this.canvas.width, this.canvas.height) // 先清除所有弹幕
        // 拿到当前视频的时间
        let currentTime = this.video.currentTime
        // 让每一个弹幕回到初始值
        this.allBarrage.forEach(item => {
            item.flag = false
            // 若当前视频时间小于弹幕出现时间,则弹幕还应该出现,所以重新初始化弹幕
            if (currentTime <= item.time) {
                item.isInit = false
            } else {
                // 否则不该出现
                item.flag = true
            }

        })
    }
}

// 创建弹幕
let canvasBarrage = new Canvas(canvas, video, { data })
// 播放视频时候开始播放弹幕
video.addEventListener('play', function (e) {
    canvasBarrage.isSuspend = false
    canvasBarrage.render()
})
// 暂停视频时候暂停弹幕
video.addEventListener('pause', function (e) {
    canvasBarrage.isSuspend = true
})
// 拖动进度条重置弹幕
video.addEventListener('seeked', function (e) {
    canvasBarrage.reset()
})

let ipt = $('#text'),
    btn = $('#submit-btn'),
    color = $('#color'),
    range = $('#range');


btn.addEventListener('click', (e) => {
    if (canvasBarrage.isSuspend) return
    let value = ipt.value.trim(),
        colorValue = color.value,
        rangeValue = range.value;
    if (value) {

        let obj = {
            text: value,
            time: video.currentTime,
            color: colorValue,
            fontSize: rangeValue
        }
        // socket.send(JSON.stringify(obj))
        // socket.onmessage = function (data) {
            // canvasBarrage.addBarrage(JSON.parse(data))
            // ipt.value = ""
        // }

        canvasBarrage.addBarrage(obj)
            ipt.value = ""

    } else {
        alert('请输入内容')
    }

})





